﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

namespace ShevaEngine2ContentPipeline.Common.ModelSplit
{
    /// <summary>
    /// Mesh split part.
    /// </summary>
    internal class MeshSplitPart
    {
        /// <summary>Mesh splitter.</summary>
        private MeshSplitter Splitter;
        /// <summary>Index.</summary>
        public int Index { get; private set; }
        /// <summary>Vertex start index.</summary>
        public int VertexStartIndex { get; private set; }
        /// <summary>Vertex end index.</summary>
        public int VertexEndIndex { get; private set; }
        /// <summary>New mesh.</summary>
        private MeshContent NewMesh;
        /// <summary>Old to new dictionary.</summary>
        private SortedDictionary<int, int> OldToNewDict;
        /// <summary>New to old dictionary.</summary>
        private SortedDictionary<int, int> NewToOldDict;
        /// <summary>Weight chanel.</summary>
        private VertexChannel<BoneWeightCollection> WeightChannel;
        /// <summary>Geometry.</summary>
        private GeometryContent Geom;
        /// <summary>Mesh bones.</summary>
        private SortedDictionary<string, object> MeshBones;
        

        /// <summary>
        /// Konstruktor.
        /// </summary>
        /// <param name="splitter">Mesh splitter object.</param>
        /// <param name="geom">Geometry.</param>
        /// <param name="vertexStartIndex">Vertex start index.</param>
        public MeshSplitPart(
            MeshSplitter splitter,
            GeometryContent geom,
            int vertexStartIndex)
        {
            this.Index = vertexStartIndex;
            this.Splitter = splitter;

            this.OldToNewDict = new SortedDictionary<int, int>();
            this.NewToOldDict = new SortedDictionary<int, int>();
            this.MeshBones = new SortedDictionary<string, object>();
            this.VertexStartIndex = vertexStartIndex;
            this.VertexEndIndex = vertexStartIndex;
            this.Geom = geom;
            this.GetWeightChannel();
        }

        /// <summary>
        /// Process method.
        /// </summary>
        /// <returns></returns>
        public MeshContent Process()
        {
            NewMesh = new MeshContent();
            NewMesh.Name = Splitter._Mesh.Name + Splitter._CurrentIndex.ToString();
            SortedDictionary<string, object> faceBones = new SortedDictionary<string, object>();
            GeometryContent newGeom = new GeometryContent();
            while (Index < Geom.Indices.Count - 1)
            {
                int[] faceIndices = new int[]
                        {
                            Geom.Indices[Index],
                            Geom.Indices[Index+1],
                            Geom.Indices[Index+2]
                        };

                for (int i = 0; i < 3; i++)
                {
                    BoneWeightCollection weightCollection = WeightChannel[
                        Geom.Indices[Index + i]];
                    foreach (BoneWeight weight in weightCollection)
                    {
                        if (!MeshBones.ContainsKey(weight.BoneName) &&
                            !faceBones.ContainsKey(weight.BoneName))
                            faceBones.Add(weight.BoneName, null);
                    }
                }
                if (MeshBones.Count + faceBones.Count > Splitter._MaxBones)
                {
                    faceBones.Clear();
                    this.VertexEndIndex = Index;
                    break;
                }

                foreach (string s in faceBones.Keys)
                    MeshBones.Add(s, null);
                faceBones.Clear();
                for (int i = 0; i < 3; i++)
                {
                    if (OldToNewDict.ContainsKey(faceIndices[i]))
                    {

                    }
                    else
                    {
                        int newIndex = NewMesh.Positions.Count;
                        NewMesh.Positions.Add(Geom.Vertices.Positions[faceIndices[i]]);

                        OldToNewDict.Add(faceIndices[i], newIndex);
                        newGeom.Vertices.Add(newIndex);
                    }
                    newGeom.Indices.Add(OldToNewDict[faceIndices[i]]);

                }
                Index += 3;
                this.VertexEndIndex = Index;
            }
            NewMesh.Geometry.Add(newGeom);
            _Finish();
            return NewMesh;
        }

        /// <summary>
        /// Finish method.
        /// </summary>
        private void _Finish()
        {
            foreach (KeyValuePair<int, int> k in OldToNewDict)
            {
                NewToOldDict.Add(k.Value, k.Key);
            }

            foreach (GeometryContent newGeom in NewMesh.Geometry)
            {
                foreach (VertexChannel channel in Geom.Vertices.Channels)
                {
                    List<object> data = new List<object>();
                    foreach (int i in newGeom.Vertices.PositionIndices)
                    {
                        data.Add(channel[NewToOldDict[i]]);
                    }
                    newGeom.Vertices.Channels.Add(
                        channel.Name, channel.ElementType, data);
                }
                newGeom.Material = Geom.Material;
                foreach (KeyValuePair<string, object> k in Geom.OpaqueData)
                {
                    if (!newGeom.OpaqueData.ContainsKey(k.Key))
                        newGeom.OpaqueData.Add(k.Key, k.Value);
                }
            }
        }

        /// <summary>
        /// Method gets weight channel.
        /// </summary>
        private void GetWeightChannel()
        {
            foreach (VertexChannel channel in Geom.Vertices.Channels)
            {
                if (channel.Name == VertexChannelNames.Weights())
                {
                    WeightChannel = (VertexChannel<BoneWeightCollection>)channel;
                    break;
                }
            }
        }
    }
}
